#include "ReplayEncoder.h"

#include <string>
#include <vector>
#include <dlfcn.h>

#include "hilog/log.h"
#include "napi/native_api.h"
// #include <multimedia/image_framework/image_pixel_map_napi.h> // Removed to avoid conflicts/missing header issues

#ifndef EXTERN_C_START
#ifdef __cplusplus
#define EXTERN_C_START extern "C" {
#define EXTERN_C_END }
#else
#define EXTERN_C_START
#define EXTERN_C_END
#endif
#endif

// Manual declaration of the NAPI interop function.
// This function is exported by libpixelmap_ndk.z.so (OpenHarmony API 12+).
// We declare it here to avoid header dependency issues.
extern "C" NativePixelMap* OH_PixelMap_InitNativePixelMap(napi_env env, napi_value source);

typedef NativePixelMap* (*OH_PixelMap_InitNativePixelMap_Func)(napi_env env, napi_value source);

namespace {
constexpr unsigned int BRIDGE_LOG_DOMAIN = 0xD005761;
constexpr const char BRIDGE_LOG_TAG[] = "ReplayEncoderNapi";

#define BRIDGE_LOG(level, fmt, ...) OH_LOG_Print(LOG_APP, level, BRIDGE_LOG_DOMAIN, BRIDGE_LOG_TAG, fmt, ##__VA_ARGS__)

std::string GetStringArgument(napi_env env, napi_value value)
{
    size_t strLen = 0;
    napi_get_value_string_utf8(env, value, nullptr, 0, &strLen);
    if (strLen == 0) {
        return std::string();
    }
    std::vector<char> buffer(strLen + 1, '\0');
    napi_get_value_string_utf8(env, value, buffer.data(), buffer.size(), &strLen);
    return std::string(buffer.data(), strLen);
}

napi_value InitBridge(napi_env env, napi_callback_info info)
{
    size_t argc = 5;
    napi_value args[5] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    if (argc < 5) {
        BRIDGE_LOG(LOG_ERROR, "init: insufficient args %{public}zu", argc);
        napi_value result;
        napi_get_boolean(env, false, &result);
        return result;
    }

    int32_t fd = -1;
    int32_t width = 0;
    int32_t height = 0;
    int32_t fps = 0;
    napi_get_value_int32(env, args[0], &fd);
    napi_get_value_int32(env, args[1], &width);
    napi_get_value_int32(env, args[2], &height);
    napi_get_value_int32(env, args[3], &fps);
    std::string mime = GetStringArgument(env, args[4]);

    BRIDGE_LOG(LOG_INFO,
               "native init fd=%{public}d width=%{public}d height=%{public}d fps=%{public}d mime=%{public}s",
               fd, width, height, fps, mime.c_str());

    bool ok = ReplayEncoder::Instance().Init(fd, width, height, fps, mime);
    BRIDGE_LOG(ok ? LOG_INFO : LOG_ERROR, "native init result ok=%{public}d", ok ? 1 : 0);

    napi_value result;
    napi_get_boolean(env, ok, &result);
    return result;
}

napi_value EncodeBridge(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    if (argc < 2) {
        BRIDGE_LOG(LOG_ERROR, "encode: insufficient args %{public}zu", argc);
        napi_value result;
        napi_get_boolean(env, false, &result);
        return result;
    }

    void *data = nullptr;
    size_t length = 0;
    napi_get_arraybuffer_info(env, args[0], &data, &length);

    int64_t timestamp = 0;
    napi_get_value_int64(env, args[1], &timestamp);
    BRIDGE_LOG(LOG_INFO, "encode: len=%{public}zu ts=%{public}lld", length, static_cast<long long>(timestamp));

    bool ok = ReplayEncoder::Instance().Encode(data, length, timestamp);
    BRIDGE_LOG(ok ? LOG_INFO : LOG_ERROR, "encode result ok=%{public}d", ok ? 1 : 0);
    napi_value result;
    napi_get_boolean(env, ok, &result);
    return result;
}

napi_value EncodePixelMapBridge(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    if (argc < 2) {
        BRIDGE_LOG(LOG_ERROR, "encodePixelMap: insufficient args %{public}zu", argc);
        napi_value result;
        napi_get_boolean(env, false, &result);
        return result;
    }

    // Use dlsym to load OH_PixelMap_InitNativePixelMap dynamically
    // This avoids linker errors if the symbol is not exported by the stub library
    static OH_PixelMap_InitNativePixelMap_Func initFunc = nullptr;
    if (!initFunc) {
        void* handle = dlopen("libpixelmap_ndk.z.so", RTLD_LAZY);
        if (handle) {
            initFunc = (OH_PixelMap_InitNativePixelMap_Func)dlsym(handle, "OH_PixelMap_InitNativePixelMap");
            if (!initFunc) {
                // Try older name or fallback?
                initFunc = (OH_PixelMap_InitNativePixelMap_Func)dlsym(handle, "OH_Image_InitNativePixelMap");
            }
            // Don't close handle to keep function pointer valid? Usually fine for system libs.
        }
        if (!initFunc) {
             BRIDGE_LOG(LOG_ERROR, "encodePixelMap: failed to dlsym OH_PixelMap_InitNativePixelMap");
             napi_value result;
             napi_get_boolean(env, false, &result);
             return result;
        }
    }

    void *nativePm = initFunc(env, args[0]);
    if (!nativePm) {
        BRIDGE_LOG(LOG_ERROR, "encodePixelMap: failed to unwrap NativePixelMap");
        napi_value result;
        napi_get_boolean(env, false, &result);
        return result;
    }

    int64_t timestamp = 0;
    napi_get_value_int64(env, args[1], &timestamp);
    BRIDGE_LOG(LOG_INFO, "encodePixelMap: ts=%{public}lld", static_cast<long long>(timestamp));

    bool ok = ReplayEncoder::Instance().EncodePixelMap(nativePm, timestamp);
    BRIDGE_LOG(ok ? LOG_INFO : LOG_ERROR, "encodePixelMap result ok=%{public}d", ok ? 1 : 0);
    
    napi_value result;
    napi_get_boolean(env, ok, &result);
    return result;
}

napi_value GetInputWindowBridge(napi_env env, napi_callback_info info)
{
    (void)info;
    void* window = ReplayEncoder::Instance().GetInputWindow();
    napi_value result;
    if (window) {
        // Return as external (pointer) or big int? External is safer for passing back to other NAPI functions.
        // But for ArkTS interaction, usually we might need a BigInt for the pointer address.
        // Let's return BigInt for flexibility.
        napi_create_bigint_uint64(env, reinterpret_cast<uint64_t>(window), &result);
    } else {
        napi_get_undefined(env, &result);
    }
    return result;
}

napi_value FinalizeBridge(napi_env env, napi_callback_info info)
{
    (void)info;
    BRIDGE_LOG(LOG_INFO, "finalize: called");
    ReplayEncoder::Instance().Finalize();
    BRIDGE_LOG(LOG_INFO, "finalize: done");
    napi_value result;
    napi_get_undefined(env, &result);
    return result;
}

} // namespace

EXTERN_C_START
static napi_value Register(napi_env env, napi_value exports)
{
    napi_property_descriptor descriptors[] = {
        {"init", nullptr, InitBridge, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"encode", nullptr, EncodeBridge, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"encodePixelMap", nullptr, EncodePixelMapBridge, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getInputWindow", nullptr, GetInputWindowBridge, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"finalize", nullptr, FinalizeBridge, nullptr, nullptr, nullptr, napi_default, nullptr},
    };

    napi_define_properties(env, exports, sizeof(descriptors) / sizeof(descriptors[0]), descriptors);
    return exports;
}
EXTERN_C_END

static napi_module g_replayEncoderModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Register,
    .nm_modname = "replay_encoder",
    .nm_priv = nullptr,
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterReplayEncoderModule()
{
    napi_module_register(&g_replayEncoderModule);
}

#undef BRIDGE_LOG





